package neurons;

import java.util.ArrayList;
import java.util.Collection;

import signal.SignalFactoryInterface;
import signal.SignalInterface;
import synapses.SynapseInterface;

public class NeuronHeavyside implements NeuronInterface {

	private Collection<SynapseInterface> inputSynapses;
	private Collection<SynapseInterface> outputSynapses;
	private Boolean synapsesAreCharged;
	private SignalFactoryInterface signalFactory;
	private InternalFunctionInterface function;
	private Double biais;

	public NeuronHeavyside(InternalFunctionInterface function,
			SignalFactoryInterface signalFactory) {
		inputSynapses = new ArrayList<SynapseInterface>();
		outputSynapses = new ArrayList<SynapseInterface>();
		synapsesAreCharged = false;
		this.signalFactory = signalFactory;
		this.function = function;
		biais = Math.random();

	}

	public void compute() {
		SignalInterface whatIaMChargedWith = collectSynapsesData();

		SignalInterface whatIDeliver = internalSignalProcess(whatIaMChargedWith);
		System.out.println(toString() + " propagating : "
				+ whatIaMChargedWith.getData());

		propagate(whatIDeliver);
	}

	private SignalInterface collectSynapsesData() {
		SignalInterface whatIaMChargedWith = signalFactory.create();
		for (SynapseInterface synapse : inputSynapses) {
			Double whatSynapseGot = synapse.getData() * synapse.getWeight();
			whatIaMChargedWith.boostData(whatSynapseGot);
		}
		System.out.println(toString() + " processing : "
				+ whatIaMChargedWith.getData() + " + biais : " + biais);
		whatIaMChargedWith.boostData(biais);
		return whatIaMChargedWith;
	}

	private SignalInterface internalSignalProcess(SignalInterface signal) {
		Double data = function.proceed(signal);

		// TODO add decision rule to form correct data

		SignalInterface whatIDeliver = signalFactory.create(data);
		return whatIDeliver;
	}

	private void propagate(SignalInterface signal) {
		for (SynapseInterface synapse : outputSynapses) {
			synapse.transmit(signal);
		}
	}

	@Override
	public void growIncomingSynapse(SynapseInterface synapseIn) {
		inputSynapses.add(synapseIn);
		// System.out.println(inputSynapses.toString());
	}

	@Override
	public void growOutGoingSynapse(SynapseInterface synapseOut) {
		outputSynapses.add(synapseOut);
		// System.out.println(outputSynapses.toString());
	}

	@Override
	public Collection<SynapseInterface> getInSynapses() {
		return inputSynapses;
	}

	@Override
	public Collection<SynapseInterface> getOutSynapses() {
		return outputSynapses;
	}

	@Override
	public void aSynapseHasBeenCharged(SynapseInterface fromExteriorSynapse) {
		synapsesAreCharged = true;
		for (SynapseInterface synapse : inputSynapses) {
			if (!synapse.isCharged()) {
				synapsesAreCharged = false;
			}
		}
		if (synapsesAreCharged) {
			compute();
		}
	}
}
